"""
二、继承
继承是面向对象程序设计中两个类之间的一种关系，是指从已有的类中派生出新的类。被继承的类称为基类或父类，继承父类的类称为子类或派生类。子类可以继承父类的属性和行为，还可以增加自己的属性和行为。例如，自行车类有品牌、外观等属性，刹车、加速等行为，而双人自行车继承了自行车类（父类）的属性和行为，还增加了后座这个属性。子类还可以改变从父类继承来的行为。例如，杂技演员使用的自行车不仅可以前进，还可以后退，这就改变了自行车类的行为。

继承使父类的代码得到重用，在继承父类提供的共同特性的基础上增加新的代码，可以大幅度减少开发工作量，并且可以很大程度地保证质量。继承分为单继承和多继承两种。

在Python中，当一个子类只有一个父类时称为单继承。定义子类的语法格式如下
    class 子类名(父类名):

子类可以继承父类的所有公有变量和公有方法，但不能继承其私有变量和私有方法。

在Python中，私有属性和方法是指在属性名或方法名前加上两个下划线__的成员。这些成员只能在类的内部访问，子类无法直接继承和访问
私有属性和方法的特点：
命名规则：以双下划线__开头（但不以双下划线结尾）
访问限制：只能在定义它们的类内部访问
名称改写：Python会对私有成员进行名称改写，实际存储名称为_类名__成员名

【例 6-11】 单继承示例。
【参考代码】
【程序说明】  上述代码中定义了Animal类，包含type和food变量，以及eat()方法；然后定义了Cat子类继承Animal类，不执行任何操作；最后创建Cat类对象，修改父类中的变量，并调用父类中的方法。从程序的运行结果可以看出，子类继承了父类的变量和方法。
【小提示】：Python中pass表示空语句，即不执行任何操作，一般用作占位语句，保持程序结构的完整性。
"""
# class Animal:					#定义Animal类
#     type = '动物'					#定义变量type
#     food = '食物'					#定义变量food
#     def eat(self):				                #定义方法输出type和food
#         print('{}吃{}'.format(self.type, self.food))
# class Cat(Animal):				                #定义Cat类继承Animal类
#     pass						#空语句
# cat = Cat()					#创建对象
# cat.eat()						#调用父类方法
# cat.type = '猫'					#修改type
# cat.food = '鱼'					#修改food
# cat.eat()						#调用父类方法


"""
2．多继承
  多继承指一个子类可以有多个父类，它继承了多个父类的特性。例如，沙发床是沙发和床的功能的组合，儿子喜欢做爸爸和妈妈喜欢做的事。
   多继承可以看作是对单继承的扩展，其语法格式如下。
语法：class 子类名(父类名1, 父类名2……):
【例 6-12】 多继承示例。
【参考代码】
【程序说明】  上述代码中定义了DadLikeDo类，包含basketball()方法，以及定义了MomLikeDo类，包含sing()方法；然后定义了继承DadLikeDo类和MomLikeDo类的子类SonLikeDo，包含dance()方法；最后创建SonLikeDo类的对象，分别调用basketball()、sing()和dance()方法。从程序运行结果可以看出，子类同时继承了多个父类的方法。
【小提示】：在Python中，如果两个父类中有同名的方法，那么调用该同名方法时会调用先继承的类中的方法。例如，如果DadLikeDo类和MomLikeDo类中有同名的方法，用“class SonLikeDo(DadLikeDo, MomLikeDo):”语句定义子类时，子类会先继承DadLikeDo类。
"""

# class DadLikeDo:  # 定义DadLikeDo类
#     def basketball(self):  # 定义打篮球方法
#         print('打篮球')
#
#
# class MomLikeDo:  # 定义MomLikeDo类
#     def sing(self):  # 定义唱歌方法
#         print('唱歌')
#     def basketball(self):  # 定义打篮球方法
#         print('打篮球1')
#
#
# # 定义SonLikeDo类，继承DadLikeDo类和MomLikeDo类  多类继承
# class SonLikeDo(DadLikeDo, MomLikeDo):
#     def dance(self):  # 定义跳舞方法
#         print('跳舞')
#
#
# son = SonLikeDo()  # 创建对象  实例化
# print('儿子喜欢做的事：')
# son.basketball()  # 调用DadLikeDo类的方法
# son.sing()  # 调用MomLikeDo类的方法
# son.dance()  # 调用SonLikeDo类的方法


"""
3．方法重写
  当父类中的方法无法满足子类需求或子类具有特有功能时，可以在子类中重写父类的方法，即子类中的方法会覆盖父类中同名的方法，以实现不同于父类的功能。
【例 6-13】重写父类的方法示例。
【参考代码】
【程序说明】  从程序运行结果可以看出，在调用Pupil类和Junior类对象的category()方法时，只调用了子类中重写的方法，不会再调用父类的category()方法。
如果需要在子类中调用父类的方法，可以使用内置函数super()或通过“父类名.方法名()”来实现。

"""
#
#
# class Student:  # 定义Student类
#     name = 'xx'  # 定义变量name
#
#     def category(self):  # 定义方法category()
#         print(self.name + '是学生')
#
#
# class Pupil(Student):  # 定义Pupil类继承Student类
#     def category(self):  # 重写category()方法
#         print(self.name, '是小学生')
#
#
# class Junior(Student):  # 定义Junior类继承Student类
#     def category(self):  # 重写category()方法
#         print(self.name, '是初中生')
#
#
# xioalan = Pupil()  # 创建Pupil类对象
# xioalan.name = '小蓝'  # 修改name
# xioalan.category()  # 调用category()方法
#
# xiaoming = Junior()  # 创建Junior类对象
# xiaoming.name = '小明'  # 修改name
# xiaoming.category()  # 调用category()方法

"""
【例6-14】  子类调用父类的方法示例。
【参考代码】
【程序说明】  以上代码中定义了Student类，在该类的__init__()方法中定义了name和age变量。然后定义了继承Student类的子类Pupil，在该类中重写了构造方法__init__()，使用super()函数调用父类的构造方法，并添加了自定义变量sex，使Pupil类既拥有自定义的sex，又拥有父类的name和age；还重写了showInfo()方法，使用父类名调用了父类的showInfo()方法输出name和age，又输出了sex。
【提示】：super关键字指向父类：在子类的静态方法中，super关键字指向父类（Foo000），用于访问父类的静态方法和静态属性。
"""
class Student:			                   #定义Student类
    def __init__(self, name, age):	                   #构造方法，传递name和age
        self.name = name
        self.age = age
    def showInfo(self):		                   #定义showInfo()方法，输出name和age
        print(f'姓名：{self.name}；年龄：{self.age}')

class Pupil(Student):		                   #定义Pupil类继承Student类
    #构造方法，传递name、age和sex
    def __init__(self, name, age, sex):
        super().__init__(name, age)	                   #调用父类构造方法
        self.sex = sex
    def showInfo(self):
        Student.showInfo(self)			     #调用父类showInfo()方法
        print('性别：{}'.format(self.sex))#输出sex
xiaolan = Pupil('小蓝', 10, '女')		     #创建对象
xiaolan.showInfo()				     #调用showInfo()方法
